//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.alibaba.nacos.client.naming.core;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import com.alibaba.nacos.client.monitor.MetricsMonitor;
import com.alibaba.nacos.client.naming.backups.FailoverReactor;
import com.alibaba.nacos.client.naming.cache.DiskCache;
import com.alibaba.nacos.client.naming.core.EventDispatcher;
import com.alibaba.nacos.client.naming.core.PushReceiver;
import com.alibaba.nacos.client.naming.net.NamingProxy;
import com.alibaba.nacos.client.naming.utils.StringUtils;
import com.alibaba.nacos.client.naming.utils.UtilAndComs;
import com.alibaba.nacos.client.utils.LogUtils;

import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.*;

public class HostReactor {
    public static final long DEFAULT_DELAY = 1000L;
    public long updateHoldInterval;
    private final Map<String, ScheduledFuture<?>> futureMap;
    private Map<String, ServiceInfo> serviceInfoMap;
    private Map<String, Object> updatingMap;
    private PushReceiver pushReceiver;
    private EventDispatcher eventDispatcher;
    private NamingProxy serverProxy;
    private FailoverReactor failoverReactor;
    private String cacheDir;
    private ScheduledExecutorService executor;

    public HostReactor(EventDispatcher eventDispatcher, NamingProxy serverProxy, String cacheDir) {
        this(eventDispatcher, serverProxy, cacheDir, false, UtilAndComs.DEFAULT_POLLING_THREAD_COUNT);
    }

    public HostReactor(EventDispatcher eventDispatcher, NamingProxy serverProxy, String cacheDir, boolean loadCacheAtStart, int pollingThreadCount) {
        this.updateHoldInterval = 5000L;
        this.futureMap = new HashMap();
        this.executor = new ScheduledThreadPoolExecutor(pollingThreadCount, new ThreadFactory() {
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setDaemon(true);
                thread.setName("com.alibaba.nacos.client.naming.updater");
                return thread;
            }
        });
        this.eventDispatcher = eventDispatcher;
        this.serverProxy = serverProxy;
        this.cacheDir = cacheDir;
        if (loadCacheAtStart) {
            this.serviceInfoMap = new ConcurrentHashMap(DiskCache.read(this.cacheDir));
        } else {
            this.serviceInfoMap = new ConcurrentHashMap(16);
        }

        this.updatingMap = new ConcurrentHashMap();
        this.failoverReactor = new FailoverReactor(this, cacheDir);
        this.pushReceiver = new PushReceiver(this);
    }

    public Map<String, ServiceInfo> getServiceInfoMap() {
        return this.serviceInfoMap;
    }

    public synchronized ScheduledFuture<?> addTask(com.alibaba.nacos.client.naming.core.HostReactor.UpdateTask task) {
        return this.executor.schedule(task, 1000L, TimeUnit.MILLISECONDS);
    }

    public ServiceInfo processServiceJSON(String json) {
        ServiceInfo serviceInfo = (ServiceInfo)JSON.parseObject(json, ServiceInfo.class);
        ServiceInfo oldService = (ServiceInfo)this.serviceInfoMap.get(serviceInfo.getKey());
        if (serviceInfo.getHosts() != null && serviceInfo.validate()) {
            if (oldService != null) {
                if (oldService.getLastRefTime() > serviceInfo.getLastRefTime()) {
                    LogUtils.NAMING_LOGGER.warn("out of date data received, old-t: " + oldService.getLastRefTime() + ", new-t: " + serviceInfo.getLastRefTime());
                }

                this.serviceInfoMap.put(serviceInfo.getKey(), serviceInfo);
                Map<String, Instance> oldHostMap = new HashMap(oldService.getHosts().size());
                Iterator var5 = oldService.getHosts().iterator();

                while(var5.hasNext()) {
                    Instance host = (Instance)var5.next();
                    oldHostMap.put(host.toInetAddr(), host);
                }

                Map<String, Instance> newHostMap = new HashMap(serviceInfo.getHosts().size());
                Iterator var15 = serviceInfo.getHosts().iterator();

                while(var15.hasNext()) {
                    Instance host = (Instance)var15.next();
                    newHostMap.put(host.toInetAddr(), host);
                }

                Set<Instance> modHosts = new HashSet();
                Set<Instance> newHosts = new HashSet();
                Set<Instance> remvHosts = new HashSet();
                List<Entry<String, Instance>> newServiceHosts = new ArrayList(newHostMap.entrySet());
                Iterator var10 = newServiceHosts.iterator();

                while(true) {
                    Entry entry;
                    Instance host;
                    String key;
                    while(var10.hasNext()) {
                        entry = (Entry)var10.next();
                        host = (Instance)entry.getValue();
                        key = (String)entry.getKey();
                        if (oldHostMap.containsKey(key) && !StringUtils.equals(host.toString(), ((Instance)oldHostMap.get(key)).toString())) {
                            modHosts.add(host);
                        } else if (!oldHostMap.containsKey(key)) {
                            newHosts.add(host);
                        }
                    }

                    var10 = oldHostMap.entrySet().iterator();

                    while(var10.hasNext()) {
                        entry = (Entry)var10.next();
                        host = (Instance)entry.getValue();
                        key = (String)entry.getKey();
                        if (!newHostMap.containsKey(key) && !newHostMap.containsKey(key)) {
                            remvHosts.add(host);
                        }
                    }

                    if (newHosts.size() > 0) {
                        LogUtils.NAMING_LOGGER.info("new ips(" + newHosts.size() + ") service: " + serviceInfo.getName() + " -> " + JSON.toJSONString(newHosts));
                    }

                    if (remvHosts.size() > 0) {
                        LogUtils.NAMING_LOGGER.info("removed ips(" + remvHosts.size() + ") service: " + serviceInfo.getName() + " -> " + JSON.toJSONString(remvHosts));
                    }

                    if (modHosts.size() > 0) {
                        LogUtils.NAMING_LOGGER.info("modified ips(" + modHosts.size() + ") service: " + serviceInfo.getName() + " -> " + JSON.toJSONString(modHosts));
                    }

                    serviceInfo.setJsonFromServer(json);
                    if (newHosts.size() > 0 || remvHosts.size() > 0 || modHosts.size() > 0) {
                        this.eventDispatcher.serviceChanged(serviceInfo);
                        DiskCache.write(serviceInfo, this.cacheDir);
                    }
                    break;
                }
            } else {
                LogUtils.NAMING_LOGGER.info("new ips(" + serviceInfo.ipCount() + ") service: " + serviceInfo.getName() + " -> " + JSON.toJSONString(serviceInfo.getHosts()));
                this.serviceInfoMap.put(serviceInfo.getKey(), serviceInfo);
                this.eventDispatcher.serviceChanged(serviceInfo);
                serviceInfo.setJsonFromServer(json);
                DiskCache.write(serviceInfo, this.cacheDir);
            }

            MetricsMonitor.getServiceInfoMapSizeMonitor().set((double)this.serviceInfoMap.size());
            LogUtils.NAMING_LOGGER.info("current ips:(" + serviceInfo.ipCount() + ") service: " + serviceInfo.getName() + " -> " + JSON.toJSONString(serviceInfo.getHosts()));
            return serviceInfo;
        } else {
            return oldService;
        }
    }

    private ServiceInfo getSerivceInfo0(String serviceName, String clusters) {
        String key = ServiceInfo.getKey(serviceName, clusters);
        return (ServiceInfo)this.serviceInfoMap.get(key);
    }

    public ServiceInfo getServiceInfoDirectlyFromServer(String serviceName, String clusters) throws NacosException {
        String result = this.serverProxy.queryList(serviceName, clusters, 0, false);
        return StringUtils.isNotEmpty(result) ? (ServiceInfo)JSON.parseObject(result, ServiceInfo.class) : null;
    }

    public ServiceInfo getServiceInfo(String serviceName, String clusters) {
        LogUtils.NAMING_LOGGER.debug("failover-mode: " + this.failoverReactor.isFailoverSwitch());
        String key = ServiceInfo.getKey(serviceName, clusters);
        if (this.failoverReactor.isFailoverSwitch()) {
            return this.failoverReactor.getService(key);
        } else {
            ServiceInfo serviceObj = this.getSerivceInfo0(serviceName, clusters);
            if (null == serviceObj) {
                serviceObj = new ServiceInfo(serviceName, clusters);
                this.serviceInfoMap.put(serviceObj.getKey(), serviceObj);
                this.updatingMap.put(serviceName, new Object());
                this.updateServiceNow(serviceName, clusters);
                this.updatingMap.remove(serviceName);
            } else if (this.updatingMap.containsKey(serviceName) && this.updateHoldInterval > 0L) {
                synchronized(serviceObj) {
                    try {
                        serviceObj.wait(this.updateHoldInterval);
                    } catch (InterruptedException var8) {
                        LogUtils.NAMING_LOGGER.error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" + clusters, var8);
                    }
                }
            }

            this.scheduleUpdateIfAbsent(serviceName, clusters);
            return (ServiceInfo)this.serviceInfoMap.get(serviceObj.getKey());
        }
    }

    public void scheduleUpdateIfAbsent(String serviceName, String clusters) {
        if (this.futureMap.get(ServiceInfo.getKey(serviceName, clusters)) == null) {
            Map var3 = this.futureMap;
            synchronized(this.futureMap) {
                if (this.futureMap.get(ServiceInfo.getKey(serviceName, clusters)) == null) {
                    ScheduledFuture<?> future = this.addTask(new com.alibaba.nacos.client.naming.core.HostReactor.UpdateTask(serviceName, clusters));
                    this.futureMap.put(ServiceInfo.getKey(serviceName, clusters), future);
                }
            }
        }
    }

    public void updateServiceNow(String serviceName, String clusters) {
        ServiceInfo oldService = this.getSerivceInfo0(serviceName, clusters);
        boolean var15 = false;

        if(serviceName.equals("DEFAULT_GROUP@@localhost"))
        {
            return;
        }

        label121: {
            try {
                var15 = true;
                String result = this.serverProxy.queryList(serviceName, clusters, this.pushReceiver.getUDPPort(), false);
                if (StringUtils.isNotEmpty(result)) {
                    this.processServiceJSON(result);
                    var15 = false;
                } else {
                    var15 = false;
                }
                break label121;
            } catch (Exception var19) {
                LogUtils.NAMING_LOGGER.error("[NA] failed to update serviceName: " + serviceName, var19);
                var15 = false;
            } finally {
                if (var15) {
                    if (oldService != null) {
                        synchronized(oldService) {
                            oldService.notifyAll();
                        }
                    }

                }
            }

            if (oldService != null) {
                synchronized(oldService) {
                    oldService.notifyAll();
                }
            }

            return;
        }

        if (oldService != null) {
            synchronized(oldService) {
                oldService.notifyAll();
            }
        }

    }

    public void refreshOnly(String serviceName, String clusters) {
        try {
            this.serverProxy.queryList(serviceName, clusters, this.pushReceiver.getUDPPort(), false);
        } catch (Exception var4) {
            LogUtils.NAMING_LOGGER.error("[NA] failed to update serviceName: " + serviceName, var4);
        }

    }

    public class UpdateTask implements Runnable {
        long lastRefTime = 9223372036854775807L;
        private String clusters;
        private String serviceName;

        public UpdateTask(String serviceName, String clusters) {
            this.serviceName = serviceName;
            this.clusters = clusters;
        }

        public void run() {
            try {
                if(this.serviceName.equals("DEFAULT_GROUP@@localhost"))
                {
                    return;
                }
                ServiceInfo serviceObj = (ServiceInfo) com.alibaba.nacos.client.naming.core.HostReactor.this.serviceInfoMap.get(ServiceInfo.getKey(this.serviceName, this.clusters));
                if (serviceObj == null) {
                    com.alibaba.nacos.client.naming.core.HostReactor.this.updateServiceNow(this.serviceName, this.clusters);
                    com.alibaba.nacos.client.naming.core.HostReactor.this.executor.schedule(this, 1000L, TimeUnit.MILLISECONDS);
                }

                if (serviceObj.getLastRefTime() <= this.lastRefTime) {
                    com.alibaba.nacos.client.naming.core.HostReactor.this.updateServiceNow(this.serviceName, this.clusters);
                    serviceObj = (ServiceInfo) com.alibaba.nacos.client.naming.core.HostReactor.this.serviceInfoMap.get(ServiceInfo.getKey(this.serviceName, this.clusters));
                } else {
                    com.alibaba.nacos.client.naming.core.HostReactor.this.refreshOnly(this.serviceName, this.clusters);
                }

                com.alibaba.nacos.client.naming.core.HostReactor.this.executor.schedule(this, serviceObj.getCacheMillis(), TimeUnit.MILLISECONDS);
                this.lastRefTime = serviceObj.getLastRefTime();
            } catch (Throwable var2) {
                LogUtils.NAMING_LOGGER.warn("[NA] failed to update serviceName: " + this.serviceName, var2);
            }

        }
    }
}
